package com.newegg.config.cluster;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.Participant;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import com.newegg.config.loader.IPHelp;
import com.newegg.config.loader.ZookeeperConfigProcessor;

@Component
@Configuration
@ConditionalOnProperty(name={"spring.cluster.enabled" }, havingValue="true")
public class NeweggClusterInfo {
	static Logger logger = Logger.getLogger(NeweggClusterInfo.class);

	@Autowired 
	Environment environment;
	
	@Value("${server.port:8080}")
	Integer serverPort;
	
	LeaderLatch latch;
	List<Node> nodes = null;
	Node leader = null;
	long lastUpdateNodes = 0L;
	long lastUpdateLeader = 0L;
	long cache_time_ms = 10000;//10s
	
	@PostConstruct
	public void init() throws Exception {
		String appname = environment.getRequiredProperty("spring.application.name");
		String node = IPHelp.getHostname() + "," + IPHelp.getIP() + "," + serverPort;
		this.latch = new LeaderLatch(ZookeeperConfigProcessor.framework, "/_cluster_" + appname, node);
		latch.start();
	}
	
	public List<Node> nodes() throws Exception{
		return nodes(false);
	}
	
	public List<Node> nodes(boolean force) throws Exception{
		if(System.currentTimeMillis() - this.lastUpdateNodes > cache_time_ms || force) {
			try {
				Collection<Participant> participants = latch.getParticipants();
				List<Node> nodes = new ArrayList<Node>();
				for (Participant participant : participants) {
					String[] node_split = participant.getId().split(",");
					Integer port = Integer.valueOf(node_split[2]);
					nodes.add(new Node(node_split[0], node_split[1], port, participant.isLeader()));
					if(participant.isLeader()) {
						this.leader = new Node(node_split[0], node_split[1], port, participant.isLeader());
						this.lastUpdateLeader = System.currentTimeMillis();
					}
				}
				this.nodes = nodes;
				this.lastUpdateNodes = System.currentTimeMillis();
			} catch (Exception e) {
				if(force) { throw e; }
				logger.warn("update participant has error", e);
			}
		}
		return this.nodes;
	}
	
	public Node leader() throws Exception {
		return leader(false);
	}
	
	public Node leader(boolean force) throws Exception {
		if(System.currentTimeMillis() - this.lastUpdateLeader > cache_time_ms || force) {
			try {
				Participant participant = latch.getLeader();
				String[] node_split = participant.getId().split(",");
				Integer port = Integer.valueOf(node_split[2]);
				this.leader = new Node(node_split[0], node_split[1], port, participant.isLeader());
				this.lastUpdateLeader = System.currentTimeMillis();
			} catch (Exception e) {
				if(force) { throw e; }
				logger.warn("update participant has error", e);
			}
		}
		return this.leader;
	}
	
	@PreDestroy
	public void onClose() throws IOException {
		this.latch.close();
	}
}
